-- treesitter
local status, treesitter = pcall(require, "nvim-treesitter.configs")
if not status then
	vim.notify("没有找到 nvim-treesitter")
	return
end

treesitter.setup({
	ensure_installed = {
		"json",
		"html",
		"css",
		"vim",
		"lua",
		"javascript",
		"typescript",
		"tsx",
		"go",
		"rust",
		"yaml",
		"markdown",
		"markdown_inline",
	},
	-- 启用代码高亮模块
	highlight = {
		enable = true,
		additional_vim_regex_highlighting = false,
	},
	-- 启用增量选择模块
	incremental_selection = {
		enable = true,
		keymaps = {
			init_selection = "<CR>",
			node_incremental = "<CR>",
			node_decremental = "<BS>",
			scope_incremental = "<TAB>",
		},
	},
})
-- 开启 Folding
vim.wo.foldmethod = "expr"
vim.wo.foldexpr = "nvim_treesitter#foldexpr()"
-- 默认不要折叠
-- https://stackoverflow.com/questions/8316139/how-to-set-the-default-to-unfolded-when-you-open-a-file
vim.wo.foldlevel = 99

-- treesitter-context
require("treesitter-context").setup({
	enable = true, -- Enable this plugin (Can be enabled/disabled later via commands)
	max_lines = 0, -- How many lines the window should span. Values <= 0 mean no limit.
	trim_scope = "outer", -- Which context lines to discard if `max_lines` is exceeded. Choices: 'inner', 'outer'
	patterns = {
		-- Match patterns for TS nodes. These get wrapped to match at word boundaries.
		-- For all filetypes
		-- Note that setting an entry here replaces all other patterns for this entry.
		-- By setting the 'default' entry below, you can control which nodes you want to
		-- appear in the context window.
		default = {
			"class",
			"function",
			"method",
			"for",
			"while",
			"if",
			"switch",
			"case",
		},
		-- Patterns for specific filetypes
		-- If a pattern is missing, *open a PR* so everyone can benefit.
		tex = {
			"chapter",
			"section",
			"subsection",
			"subsubsection",
		},
		rust = {
			"impl_item",
			"struct",
			"enum",
		},
		scala = {
			"object_definition",
		},
		vhdl = {
			"process_statement",
			"architecture_body",
			"entity_declaration",
		},
		markdown = {
			"section",
		},
		elixir = {
			"anonymous_function",
			"arguments",
			"block",
			"do_block",
			"list",
			"map",
			"tuple",
			"quoted_content",
		},
		json = {
			"pair",
		},
		yaml = {
			"block_mapping_pair",
		},
	},
	exact_patterns = {
		-- Example for a specific filetype with Lua patterns
		-- Treat patterns.rust as a Lua pattern (i.e "^impl_item$" will
		-- exactly match "impl_item" only)
		-- rust = true,
	},
	-- [!] The options below are exposed but shouldn't require your attention,
	--     you can safely ignore them.

	zindex = 20, -- The Z-index of the context window
	mode = "cursor", -- Line used to calculate context. Choices: 'cursor', 'topline'
	-- Separator between context and content. Should be a single character string, like '-'.
	-- When separator is set, the context will only show up when there are at least 2 lines above cursorline.
	separator = nil,
})

-- mason
local masonOk, mason = pcall(require, "mason")
if not masonOk then
	vim.notify("没有找到 mason")
	return
end
mason.setup()

local ensure_installed = {
	"gopls",
	"golangci-lint",
	"delve",
	"rust-analyzer",
	"vue-language-server",
	"lua-language-server",
	"stylua",
	"beautysh",
	"pyright",
}

vim.api.nvim_create_user_command("MasonInstallAll", function()
	vim.cmd("MasonInstall " .. table.concat(ensure_installed, " "))
end, {})

-- local util = require("lspconfig/util")
--
-- local lastRootPath = nil
-- local gopath = os.getenv("GOPATH")
-- if gopath == nil then
-- 	gopath = ""
-- end
-- local gopathmod = gopath .. "/pkg/mod"

require("core.lsp.go")

local servers = {
	-- ["gopls"] = {
	-- 	cmd = { "gopls" },
	-- 	filetypes = { "go", "gomod" },
	-- 	-- root_dir = util.root_pattern("go.work", "go.mod", ".git"),
	-- 	-- root_dir = function(fname)
	-- 	-- 	local fullpath = vim.fn.expand(fname, ":p")
	-- 	-- 	if (string.find(fullpath, gopathmod) or string.find(fullpath, "vendor")) and lastRootPath ~= nil then
	-- 	-- 		return lastRootPath
	-- 	-- 	end
	-- 	-- 	lastRootPath = util.root_pattern("go.mod", ".git")(fname)
	-- 	-- 	return lastRootPath
	-- 	-- end,
	-- 	settings = {
	-- 		gopls = {
	-- 			allExperiments = true,
	-- 			experimentalPostfixCompletions = true,
	-- 			analyses = {
	-- 				unusedparams = true,
	--          unusedwrite = true,
	-- 			},
	-- 			hints = {
	-- 				constantValues = true,
	-- 				assignVariableTypes = true,
	--          -- parameterNames = true,
	-- 			},
	-- 			staticcheck = true,
	-- 			usePlaceholders = true,
	-- 		},
	-- 	},
	-- },
	["lua_ls"] = {
		settings = {
			Lua = {
				runtime = {
					-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
					version = "LuaJIT",
				},
				diagnostics = {
					-- Get the language server to recognize the `vim` global
					globals = { "vim" },
				},
				workspace = {
					-- Make the server aware of Neovim runtime files
					library = vim.api.nvim_get_runtime_file("", true),
				},
				-- Do not send telemetry data containing a randomized but unique identifier
				telemetry = {
					enable = false,
				},
			},
		},
	},
	["rust_analyzer"] = {},
	["volar"] = {
		filetypes = { "typescript", "javascript", "javascriptreact", "typescriptreact", "vue", "json" },
	},
	["pyright"] = {},
	["yamlls"] = {},
}

-- lsp
local lspconfig = require("lspconfig")
local on_attach = function(client, bufnr)
	client.server_capabilities.document_formatting = false
	client.server_capabilities.document_range_formatting = false

	-- Set autocommands conditional on server_capabilities
	vim.cmd([[
	  autocmd CursorHold * set updatetime=800
    augroup lsp_document_highlight
		  autocmd! * <buffer>
		  autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
		  autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
    augroup END
  ]])
end

for lsp, opt in pairs(servers) do
	opt.on_attach = on_attach

	lspconfig[lsp].setup(opt)
end

local status, null_ls = pcall(require, "null-ls")
if not status then
	vim.notify("没有找到 null-ls")
	return
end

local builtins = null_ls.builtins
null_ls.setup({
	debug = true,
	sources = {
		-- Lua
		builtins.formatting.stylua,
		-- golang
		builtins.diagnostics.golangci_lint.with({
			extra_args = {
				"-E",
				"bodyclose",
				"-E",
				"dupl",
				"-E",
				"errorlint",
				"-E",
				"exportloopref",
				"-E",
				"funlen",
				"-E",
				"gocognit",
				"-E",
				"goconst",
				"-E",
				"gocyclo",
				"-E",
				"goprintffuncname",
				"-E",
				"gosec",
				"-E",
				"prealloc",
				"-E",
				"revive",
				"-E",
				"stylecheck",
				"--concurrency",
				"8",
			},
		}),
		builtins.formatting.goimports,
		-- rust
		builtins.formatting.rustfmt,
		-- codeaction
		-- builtins.code_actions.refactoring,
		-- front
		builtins.formatting.prettier,

		null_ls.builtins.formatting.beautysh,
	},
})
require("core.lsp.custom_null_ls")

-- lspsage
require("core.lsp.lspsaga-config")

-- blankline
require("indent_blankline").setup({
	-- for example, context is off by default, use this to turn it on
	show_current_context = true,
	show_current_context_start = true,
})

-- outline
require("symbols-outline").setup()

-- trouble

local ok, trouble = pcall(require, "trouble")
if ok then
	trouble.setup({
		-- your configuration comes here
		-- or leave it empty to use the default settings
		-- refer to the configuration section below
	})
else
	vim.notify("没有找到 trouble")
end

-- fidget
-- lsp 进度条
require("fidget").setup({
	align = {
		bottom = false, -- align fidgets along bottom edge of buffer
		right = true, -- align fidgets along right edge of buffer
	},
})

-- debug
require("core.lsp.dap")

-- do something fun
local signs = {}
local len = 1000
math.randomseed(os.time())
local num = math.random(len)
if num < len / 3 then
	signs = { Error = "🙀", Warn = "😿", Hint = "😾", Info = "😺" }
elseif len / 3 <= num and num <= (len / 3 * 2) then
	signs = { Error = "😡", Warn = "😥", Hint = "😤", Info = "😐" }
else
	signs = { Error = " ", Warn = " ", Hint = " ", Info = " " }
end

for type, icon in pairs(signs) do
	local hl = "DiagnosticSign" .. type
	vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = hl })
end
